//
// create the GOOP Object
//
var GOOP = {
	version:.9,
	dom:false, // is the DOM ready?
	load:true // has the window loaded
};


//
// initator, called automatically when the DOM is ready
//
GOOP.onDom = function() {
	GOOP.dom = true;
	GOOP.extend(document.body,['events','dimensions']);
	DOM.init();
};

//
// window onload, called automatically when the window has loaded
//
GOOP.onLoad = function() {
	GOOP.load = false;
	Reflect(window);
}


//
// extends the element(s) attaching the GOOP methods
//
GOOP.extend = function(element,methods) {

	// they didn't want it to be extended
	if(methods === false)
		return element;

	// find out what methods to attach
	methods = methods || ['styles','events','dimensions','effects'];
	if(methods.indexOf('basic') == -1)
		methods.unshift('basic');

	if(isArray(element))

		// array of elements
		element.each(function(el) {
			GOOP.extend(el,methods);
		});

	else

		// individual element
		methods.each(function(method) {
			if(!element['goop_' + method]) {
				for(m in GOOP[method])
					element[m] = GOOP[method][m];
				element['goop_' + method] = true;
			}
		});

	return element;
};


//
// basic, or most commonly used from the other extended types
//
GOOP.basic = {

	//
	// get elements
	// thank you simon willision (http://simonwillison.net/2003/Mar/25/getElementsBySelector/)
	//
	getElements:function(search,methods) {

		var group = [],
			context,
			element = this,
			el;

		search.split(',').each(function(sel) {
	
			context = [element];

			sel.trim().split(' ').each(function(s) {

				// don't continue if this is the body we are going to look through
				if(s.toLowerCase() == 'body')
					return;

				// ids
				if(s.indexOf('#') > -1) {
					el = $(s.split('#')[1],false);
					if(!el)
						context = [];
					else
						context = [el];
					return;
				}

				// classes
				if(s.indexOf('.') > -1) {
					var b = s.split('.'),
					t = (b[0] || '*').toUpperCase(),
					c = b[1],
					filter = [];
					
					context.each(function(el) {
						toArray(el.getElementsByTagName(t) || []).each(function(el) {
							if(el.className && el.className.match(new RegExp('(^|\\s)' + c + '(?:\\s|$)')) != null)
								filter.push(el);
						});
					});

					context = filter;
					return;
				}

				// code to deal with attribute selectors
				/* That revolting regular expression explained
				/^(\w+)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/
				  \---/  \---/\-------------/    \-------/
					|      |         |               |
					|      |         |           The value
					|      |    ~,|,^,$,* or =
					|   Attribute
				   Tag
				*/
				if(s.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)) {
					var t = (RegExp.$1 || '*').toUpperCase(),
					an = (RegExp.$2).toLowerCase(),
					ao = RegExp.$3,
					av = RegExp.$4,
					filter = [],
					ftn; // this function will be used to filter the elements

					context.each(function(el) {
						filter = filter.concat(toArray(el.getElementsByTagName(t) || []));
					});

					if(Browser.name == 'Explorer' && an == 'class')
						an = 'className'; // for IE bug

					if(ao == '=')
						// equality
						ftn = function(e) {
							return e.getAttribute(an) == av;
						}
					else if(ao == '~')
						// match one of the space seperated words
						ftn = function(e) {
							return e.getAttribute(an).match(new RegExp('\\b' + av + '\\b'));
						}
					else if(ao == '|')
						// match start with value followed by options hyphen
						ftn = function(e) {
							return e.getAttribute(an).match(new RegExp('^' + av + '-?'));
						}
					else if(ao == '^')
						// match starts with
						ftn = function(e) {
							return e.getAttribute(an).indexOf(av) == 0;
						}
					else if(ao == '$')
						// match ends with value - fails with warning in Opera 7
						ftn = function(e) {
							return e.getAttribute(an).lastIndexOf(av) == e.getAttribute(an).length - av.length;
						}
					else if(ao == '*')
						// match ends with value
						ftn = function(e) {
							return e.getAttribute(an).indexOf(av) > -1;
						}
					else
						// just test for existence of attribute
						ftn = function(e) {
							return e.getAttribute(an);
						}

					context = [];
					filter.each(function(el) {
						if(!el.getAttribute(an))
							return; // acts as a break
						if(ftn(el))
							context.push(el);
					});

					return;
				}

				// if we made it here, its just a tag name
				var filter = [],
					s = s.toUpperCase();
				context.each(function(el) {
					filter = filter.concat(toArray(el.getElementsByTagName(s) || []));
				});
				context = filter;

			});

			group.push(context);
		});

		context = [];
		group.each(function(g) {
			context = context.concat(g);
		})

		return context.length > 0 ? GOOP.extend(context,methods) : null;
	},

	getElement:function(search,methods) {
		var elements = this.getElements(search,methods);
		return elements != null ? elements[0] : null;
	},


	//
	// set and get properties
	//
	set:function(attr,value,append) {

		if(typeof attr != 'string') {
			for(var i in attr)
				this.set(i,attr[i]);
			return;
		}

		if(attr == 'html')
			this.innerHTML = (append ? this.innerHTML : '') + value;
		else if(attr == 'class')
			this.className = value;
		else if(attr == 'id')
			this.id = value;
		else if(attr == 'text')
			if(this.innerText)
				this.innerText = (append ? this.innerText : '') + value;
			else
				this.textContent = (append ? this.textContent : '') + value;
		else
			(this.hasOwnProperty && this.hasOwnProperty(attr)) ? this[attr] = value : this.setAttribute(attr,'' + value);

		return this;
	},

	get:function(attr) {

		if(attr == 'html')
			return this.innerHTML;
		else if(attr == 'class')
			return this.className || '';
		else if(attr == 'id')
			return this.id || '';
		else if(attr == 'text')
			return this.innerText ? this.innerText : this.textContent;
		else
			return (this.hasOwnProperty && this.hasOwnProperty(attr)) ? this[attr] : this.getAttribute(attr);
	},

	hasClass:function(name) {
		return this.get('class').match(new RegExp('(^|\\s)' + name + '(?:\\s|$)')) != null ? true : false;
	},

	addClass:function(name) {
		if(!this.hasClass(name))
			this.set('class',(this.get("class") + ' ' + name).trim());
		return this;
	},

	removeClass:function(name) {
		this.set('class',this.get('class').replace(new RegExp('(^|\\s)' + name + '(?:\\s|$)'), '$1'));
		return this;
	},


	//
	// inserters - replace, adopt, grab, inject, wraps, and destroy
	// huge thanks to MooTools
	//
	_inserters:function(where,context,element) {
		if(where == 'after') {
			if(!element.parentNode)
				return;
			var next = element.nextSibling;
			(next) ? element.parentNode.insertBefore(context,next) : element.parentNode.appendChild(context);
		} else if(where == 'before') {
			if(element.parentNode)
				element.parentNode.insertBefore(context,element);
		} else if(where == 'bottom') {
			element.appendChild(context);
		} else if(where == 'top') {
			var first = element.firstChild;
			(first) ? element.insertBefore(context,first) : element.appendChild(context);
		}
	},

	replaces:function(element) {
		element.parentNode.replaceChild(this,element);
		return this;
	},

	adopt:function(element) {
		if(isArray(element)) {
			var p = this;
			element.each(function(el) {
				p.adopt(el);
			});
		} else
			this.appendChild(element);
		return this;
	},

	grab:function(element,where) {
		this._inserters((where || 'bottom'),element,this);
		return this;
	},

	inject:function(element,where) {
		this._inserters((where || 'bottom'),this,element);
		return this;
	},

	wraps:function(element,where) {
		if(isArray(element)) {
			var p = this;
			element.each(function(el) {
				p.wraps(el,where);
			});
		} else
			this.replaces(element).grab(element,where);
		return this;
	},

	destroy:function() {
		return this.parentNode.removeChild(this);
	},

	empty:function() {
		while(this.hasChildNodes())
			this.removeChild(this.firstChild);
		return this;
	},


	//
	// set and get misc properties and values in storage
	// thank you MooTools
	//
	_set:function(property,value) {
		this.storage = this.storage || [];

		if(typeof property != 'string') {
			for(var v in property)
				this._set(v,property[v]);
			return;
		}

		this.storage[property] = value;
		return this;
	},

	_get:function(property) {
		if(!this.storage)
			return false;
		if(this.storage[property] == 0 || this.storage[property] == '0')
			return 0;
		return this.storage[property] || false;
	}

};



//
// styles
// thank you MooTools
//
GOOP.styles = {

	setStyle:function(style,value) {

		if(style == 'opacity') {
			if(!this.currentStyle || !this.currentStyle.hasLayout)
				this.style.zoom = 1;
			this.style.opacity = value * .01;
			this.style.filter = 'alpha(opacity=' + value + ')';
			value = (value < 0 ? 0 : (value > 100 ? 100 : value)); // can't be > 100 || < 0
			this._set('opacity',value.toInt()); // stored for reference
		} else if(style == 'float') {
			if(Browser.name == 'Explorer')
				this.style.styleFloat = value;
			else
				this.style.cssFloat = value;
		} else
			this.style[style.camelCase()] = value + (typeof value === 'number' && style.camelCase() !== 'zIndex' ? 'px' : '');

		return this;
	},

	setStyles:function(styles) {
		for(var s in styles)
			this.setStyle(s,styles[s]);
		return this;
	},

	getStyle:function(style) {

		if(style == 'opacity') {
			if(this._get('opacity') === 0)
				return 0;
			if(Browser.name == 'Explorer')
				return this._get('opacity') || this.currentStyle[style.camelCase()] || this.style.filter;
			else
				return this._get('opacity') || document.defaultView.getComputedStyle(this,'').getPropertyValue(style) || this.style.opacity;
		} else if(style == 'float') {
			return Browser.name == 'Explorer' ? this.style.styleFloat : this.style.cssFloat;
		} else {
			if(Browser.name == 'Explorer')
				return this.style[style.camelCase()] || this.currentStyle[style.camelCase()];
			else
				return this.style[style.camelCase()] || document.defaultView.getComputedStyle(this,'').getPropertyValue(style);
		}

	},

	getStyles:function(styles) {
		var returned = {}, p = this;
		styles.each(function(s) {
			returned[s] = p.getStyle(s);
		});
		return returned;
	}

};


//
// events to attach to elements
//
GOOP.events = {

	addEvent:function(type,ftn) {
		if(this.addEventListener) {

			// HUGE thanks to "incoherent babble" at http://blog.stchur.com/2007/03/15/mouseenter-and-mouseleave-events-for-firefox-and-other-non-ie-browsers/

			if(type.match(/mouseenter|mouseleave/i))
				this.addEventListener((type.match(/mouseenter/i) ? 'mouseover' : 'mouseout'),this._mouseEvent(ftn),false);
			else
				this.addEventListener(type,ftn,false);

		} else if(this.attachEvent) {
			var p = this;
			this._set('event_' + type,function() {return ftn.apply(p,[window.event]);});
			this.attachEvent('on' + type,this._get('event_' + type));
		} else
			this['on' + type] = ftn;
		return this;
	},

	addEvents:function(events) {
		for(var e in events)
			this.addEvent(e,events[e]);
		return this;
	},

	removeEvent:function(type,ftn) {
		if(this.removeEventListener)
			this.removeEventListener(type,ftn,false);
		else if(this.detachEvent) {
			if(this._get('event_' + type))
				this.detachEvent('on' + type,this._get('event_' + type));
		} else
			this['on' + type] = null;
		return this;
	},

	removeEvents:function(events) {
		for(var e in events)
			this.removeEvent(e,events[e]);
		return this;
	},

	doEvent:function(type) {
		if(document.createEvent) {
			var e = document.createEvent('HTMLEvents');
			e.initEvent(type,true,true);
			this.dispatchEvent(e);
		} else if(document.createEventObject) {
			if(type === 'resize')
				this._get('event_' + type)();
			else
				this.fireEvent('on' + type,document.createEventObject());
		} else
			this['on' + type]();
		return this;
	},

	doEvents:function(events) {
		var p = this;
		events.each(function(e) {
			p.doEvent(e);
		});
		return this;
	},

	// used if mouseenter and mouseleave events are being used
	_mouseEvent:function(ftn) {
		return function(e) {
			if(this === e.relatedTarget || this._childOf(e.relatedTarget))
				return;
			ftn.call(this,e);
		}
	},

	_childOf:function(target) {
		if(this === target)
			return false;
		while(target && target !== this)
			target = target.parentNode;
		return target === this;
	}

};


//
// dimensions
// thanks MooTools
//
GOOP.dimensions = {

	getSize:function() {
		if(/body/i.test(this.tagName)) {
			var b = document.body,
				e = document.documentElement;
			return {
					width:self.innerWidth || document.documentElement.clientWidth,
					height:self.innerHeight || document.documentElement.clientHeight,
					real_width:Math.max(Math.max(b.scrollWidth,e.scrollWidth),Math.max(b.clientWidth,e.clientWidth)),
					real_height:Math.max(Math.max(b.scrollHeight,e.scrollHeight),Math.max(b.clientHeight,e.clientHeight))
					};
		} else
			return {width:this.offsetWidth, height:this.offsetHeight};
	},

	getScroll:function() {
		if(/body|html/i.test(this.tagName))
			return {left:window.pageYOffset || document.body.scrollLeft || document.documentElement.scrollLeft, top:window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop};
		else
			return {left:this.scrollLeft, top:this.scrollTop};
	},

	getPosition:function() {
		var s = this.getScroll(),
			l = this.offsetLeft,
			t = this.offsetTop,
			p = this.offsetParent;
		while(p != null) {
			l += p.offsetLeft;
			t += p.offsetTop;
			p = p.offsetParent;
		}
		return {left:l + s.left, top:t + s.top};
	},

	getCoordinates:function() {
		var s = this.getSize(),
			p = this.getPosition();
		return {left:p.left, top:p.top, width:s.width, height:s.height, right:p.left + s.width, bottom:p.top + s.height};
	},

	// thank you - http://snippets.dzone.com/posts/show/913
	hitTest:function(el) {
		var a = this.getCoordinates(),
		b = $(el,['dimensions']).getCoordinates();
		return ((a.left == b.left || (a.left > b.left ? a.left <= b.right : b.left <= a.right)) && (a.top == b.top || (a.top > b.top ? a.top <= b.bottom : b.top <= a.bottom)));
	}

};


//
// effects
// thank you MooTools
//
GOOP.effects = {

	fade:function(action,options) {

		options = options || {};
		action = action || 'out';

		if(action == 'in') {
			if(!this.getStyle('opacity'))
				this.setStyle('opacity',0);
		} else if(action == 'out') {
			if(!this.getStyle('opacity'))
				this.setStyle('opacity',100);
		}

		this.tween('opacity',(action == 'in' ? 0 : (options.opacity || 100)),(action == 'out' ? 0 : (options.opacity || 100)),options);

		return this;

	},

	hide:function(options) {
		options = options || {};
		options.direction = options.direction || 'top';
		options.duration = 1;
		this.slide("out",options);
		return this;
	},

	show:function(options) {
		options = options || {};
		options.direction = options.direction || 'top';
		options.duration = 1;
		this.slide("in",options);
		return this;
	},

	toggle:function(options) {

		options = options || {};
		options.direction = options.direction || 'top';

		var pos = this.getStyle('margin-' + options.direction);
		this.slide(pos == '' || pos.toInt() >= 0 ? 'out' : 'in',options);

		return this;

	},

	slide:function(action,options) {

		options = options || {};
		options.direction = options.direction || 'top';
		action = action || 'out';

		// make a parent div to wrap it in
		var p = GOOP.extend(this.parentNode,[]);
		if(p.tagName != 'DIV' || !p.hasClass('slide'))
			new Element('div',{
				'class':'slide',
				setStyles:{
					margin:0,
					padding:0,
					overflow:'hidden'
				}
			}).wraps(this);

		// get current position
		var pos = this.getStyle('margin-' + options.direction);
		if(pos == '') {
			this.setStyle('margin-' + options.direction,0);
			pos = 0;
		}

		// check if we should slide
		if(pos.toInt() <= 0 && action == 'in')
			this.tween('margin-' + options.direction,pos.toInt(),0,options);
		else if(pos.toInt() >= 0 && action == 'out')
			this.tween('margin-' + options.direction,0,-this.getSize()[options.direction == 'top' || options.direction == 'bottom' ? 'height' : 'width'],options);

		return this;

	},

	morph:function(styles,options) {
		options = options || {};
		for(var i in styles)
			this.tween(i,styles[i][0],styles[i][1],options);
		return this;
	},

	tween:function(style,from,to,options) {

		options = options || {};
		
		var p = this, // acts as parent
			interval = Math.ceil(1000 / (options.fps || 50)),
			tf = Math.ceil((options.duration || 500) / interval),
			step,
			flag;
		p.started = options.start || function() {}; // function to execute at the start
		p.completed = options.complete || function() {}; // function to execute once completed

		// for color, background-color, and border-color - the transition is a bit different
		if(style == 'color' || style == 'background-color' || style == 'border-color')
			var brgb = Color.toRGB(from), ergb = Color.toRGB(to),
				step = [(ergb[0] - brgb[0]) / tf, (ergb[1] - brgb[1]) / tf, (ergb[2] - brgb[2]) / tf, 0];
		else
			step = (to.toInt() - from.toInt()) / tf;

		// make a tweentimer array
		if(!this.tweentimer)
			this.tweentimer = [];

		// execute at start
		this.started();

		// firstly, stop the timer of that tweened style so we don't leak memory, then set the style
		this.stopTween(style).setStyle(style,from).tweentimer[style] = $interval(interval,function() {

			flag = false;

			// for color, background-color, and border-color
			if(style == 'color' || style == 'background-color' || style == 'border-color') {

				brgb[0] += step[0],
				brgb[1] += step[1],
				brgb[2] += step[2];

				p.setStyle(style,Color.toHEX(brgb));

				// are we done with the colors?
				if((brgb[0] + step[0]) >= ergb[0] && (brgb[1] + step[1]) >= ergb[1] && (brgb[2] + step[2]) >= ergb[2] && !flag) {
					if(step[3] == 2)
						flag = true;
					step[3] = 1;
				} else if((brgb[0] + step[0]) <= ergb[0] && (brgb[1] + step[1]) <= ergb[1] && (brgb[2] + step[2]) <= ergb[2] && !flag) {
					if(step[3] == 1)
						flag = true;
					step[3] = 2;
				}

				if(flag) {
					p.setStyle(style,to);
					p.stopTween(style);
				}

			} else {

				// get position
				var pos = p.getStyle(style).toFloat();

				// move it
				if(step > 0 && (pos + step) < to)
					p.setStyle(style,pos + step);
				else if(step < 0 && (pos + step) > to)
					p.setStyle(style,pos + step);
				else {
					p.setStyle(style,to);
					p.stopTween(style);
					flag = true;
				}

			}

			// done moving
			if(flag)
				if(!p.anyTweens())
					p.completed(); // we do this so in the complete function we can use "this" or the passed variable/object
		});

		return this;
	},

	stopTween:function(style) {
		if(!this.tweentimer)
			return;
		$clear(this.tweentimer[style]);
		this.tweentimer[style] = -1;
		return this;
	},

	stopTweens:function(styles) {
		if(styles == "all")
			for(var i in this.tweentimer)
				this.stopTween(i);
		else {
			var p = this;
			styles.each(function(s) {
				p.stopTween(s);
			});
		}
		return this;
	},

	anyTweens:function() {
		if(!this.tweentimer)
			return false;
		var i,flag = false;
		for(i in this.tweentimer)
			flag = this.tweentimer[i] > -1 ? true : flag;
		return flag;
	}

};


//
// global functions
//
function $(id,methods) {

	var el, i;

	if(typeof id == 'string') {

		el = document.getElementById(id) || null;

		// for ie only, becareful of the 'name' and 'id'
		if(Browser.name == 'Explorer' && el != null) {
			if(el.id != id) {
				for(i = 0; i < document.all[id].length; i++) {
					if(document.all[id][i].id == id)
						el = document.all[id][i];
				}
			}
		}

		if(el != null)
			GOOP.extend(el,methods);

	} else if(typeof id == 'object')
		el = GOOP.extend(id,methods);

	return el;
}

function $$(search,methods) {
	return document.body.getElements(search,methods);
}

function $time() {
	return (new Date).getTime();
}

function $rand(n1,n2,w) {
	var n = (n2 * Math.random()) + n1;
	return !w ? Math.ceil(n) : n;
}

function $focus(form,name) {

	if(!form)
		form = document.forms[0];
	else
		form = document.forms[form];

	if(!form)
		return;

	if(name) {
		if(form[name])
			form[name].focus();
	} else {
		for(var i in form) {
			if(!form[i]) continue;
			if(!form[i].type) continue;
			if(form[i].type.toLowerCase() == 'text' || form[i].type.toLowerCase() == 'textarea') {
				form[i].focus();
				break;
			}
		}
	}

}

function $timeout(mil,ftn) {
	return setTimeout(ftn,mil);
}
function $interval(mil,ftn) {
	return setInterval(ftn,mil);
}
function $clear(t) {
	clearTimeout(t);
	clearInterval(t);
}

function jump2field(el,to,form) {

	// find form if not passed
	if(form)
		form = document.forms[form];
	else {
		form = el.parentNode;
		while(form.tagName != 'FORM')
			form = form.parentNode;
	}

	// never found, and size is not set
	if(!form && (!el.size || !el.maxLength))
		return;

	// jump to field if we meet the length
	if(el.value.length == (el.maxLength > -1 ? el.maxLength : el.size))
		if(form[to])
			form[to].focus();
}

function isArray(obj) {
	return Object.prototype.toString.apply(obj) === '[object Array]';
}

function toArray(array) {
	for(var i = 0, l = array.length || 0, a = []; i < l; i++)
		a.push(array[i]);
	return a;
}


// huge thanks to supersleight
var fixPNG = {

	path:'transparent.gif',
	active:true,
	_class:[],
	_id:[],

	init:function(element,mode) {
		element = element || document;
		if(Browser.name != 'Explorer')
			return;
		if(Browser.version > 6)
			return;

		var i, j, el, flag;

		for(i = element.all.length - 1, el = null; el = element.all[i]; i--) {

			flag = false;

			// ignores
			for(j = 0; j < this._class.length; j++) {
				if(el.className.match(new RegExp('(^|\\s)' + this._class[j] + '(?:\\s|$)')) != null)
					flag = true;
			}
			for(j = 0; j < this._id.length; j++) {
				if(el.id == this._id[j])
					flag = true;
			}
			if(flag) continue;

			// backgrounds
			if(el.currentStyle.backgroundImage.match(/\.png/i) != null)
				fixPNG.bg_fix(el,mode);

			// images
			if(el.tagName.toLowerCase() == 'img' && el.src.match(/\.png/i) != null)
				fixPNG.el_fix(el,mode);

			// apply position to active elements
			if(this.active && (el.tagName.toLowerCase() == 'a' || el.tagName.toLowerCase() == 'input') && el.style.position === '')
				el.style.position = 'relative';

		}
	},

	bg_fix:function(element,mode) {
		var bg = element.currentStyle.backgroundImage,
			src = bg.substring(5,bg.length-2); // strips the src from inside: url("path/to/file.png")
		if(element.currentStyle.backgroundRepeat == 'no-repeat')
			mode = mode || 'crop';
		element.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + src + '"), sizingMethod="' + (mode || 'scale') + '")';
		element.style.backgroundImage = 'url(' + this.path + ')';
	},

	el_fix:function(element,mode) {
		var src = element.src;
		element.style.width = element.width + 'px';
		element.style.height = element.height + 'px';
		element.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + src + '",sizingMethod="' + (mode || 'scale') + '")';
		element.src = this.path;
	}

};


//
// creates an element and gets extended by GOOP
//
function Element(tag,options,methods) {

	options = options || {};

	var el = document.createElement(tag.toUpperCase()),
		op;
	GOOP.extend(el,methods);

	for(op in options) {
		if(typeof op == 'string') {
			if(typeof el[op] == 'function')
				el[op](options[op]);
			else
				el.set(op,options[op]);
		} else if(typeof op == 'object') {
			el[op](options[op]);
		}
	}

	return el;
}


//
// swf
// thank you MooTools
//
function SWF(path,options) {

	options = options || {};
	options = {
		id:options.id || 'swf_' + $time(),
		container:options.container || null,
		width:options.width || 1,
		height:options.height || 1,
		flashVars:options.flashVars || '',
		properties:options.properties || {},
		params:options.params || {
			quality:'high',
			allowScriptAccess:'always',
			wMode:'transparent'
		}
	};

	options.properties.id = options.id;
	options.properties.width = options.width;
	options.properties.height = options.height;
	options.params.flashVars = options.flashVars;

	if(Browser.name == 'Explorer') {
		options.properties.classid = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000';
		options.params.movie = path;
	} else {
		options.properties.type = 'application/x-shockwave-flash';
		options.properties.data = path;
	}

	var swf = '<object',
		p;
	for(p in options.properties)
		swf += ' ' + p + '="' + options.properties[p] + '"';
	swf += ' />';

	for(p in options.params)
		swf += '<param name="' + p + '" value="' + options.params[p] + '" />';
	swf += '</object>';

	// write it inside of a new div
	$(options.container).adopt(new Element("div",{},[]).set("html",swf));

};


//
// event, makes the event crossbrowser
// HUGE thanks to MooTools
//
function Event(e,w) {

	var code,
		page,
		client,
		wheel,
		rightClick,
		w = w || window;

	if(e.type.match(/key/i))
		code = e.which || e.keyCode;
	else if(e.type.match(/(click|mouse|menu)/i)) {
		page = {
			x:(e.pageX) ? e.pageX : e.clientX + document.body.getScroll().left,
			y:(e.pageY) ? e.pageY : e.clientY + document.body.getScroll().top
		};
		client = {
			x:(e.pageX) ? e.pageX - w.pageXOffset : e.clientX,
			y:(e.pageY) ? e.pageY - w.pageYOffset : e.clientY
		}
		if(e.type.match(/dommousescroll|mousewheel/i))
			wheel = (e.wheelDelta) ? el.wheelDelta / 120 : -(e.detail || 0) / 3;
		rightClick = (e.which == 3) || (e.button == 2);
	}

	return {
		event:e,
		type:e.type,
		wheel:wheel,
		page:page,
		client:client,
		rightClick:rightClick,
		code:code
	}

}


//
// DOM ready, automatically is called
// thank you http://www.thefutureoftheweb.com/blog/adddomloadevent
//
var DOM = {
	ftns:[],
	ready:function(ftn) {
		if(GOOP.dom) ftn();
		else this.ftns.push(ftn);
	},
	init:function() {
		while(DOM.ftns.length > 0)
			(DOM.ftns.shift())();
	}
};
(function() {

	// mozilla
	if(document.addEventListener)
		document.addEventListener('DOMContentLoaded',GOOP.onDom,false);

	// safari
	if(/khtml|webkit/i.test(navigator.userAgent))
		var setInt = $interval(10,function() {
			if(/loaded|complete/i.test(document.readyState)) {
				$clear(setInt);
				GOOP.onDom();
			}
		});

	// ie
	/*@cc_on @*/
	/*@if (@_win32 || @_win64) @*/
		document.write('<script id="__ie_domready__" defer src=//0><\/scr'+'ipt>');
		document.getElementById('__ie_domready__').onreadystatechange = function() {
			if(this.readyState == 'complete')
				GOOP.onDom();
		};
	/*@end @*/

})();


//
// simple ajax
// thank you XHConn
//
function Ajax(options) {

	options = options || {};
	if(!options.url)
		return;

	options.start = options.start || function() {};
	options.complete = options.complete || function() {};
	options.error = options.error || function() {};
	options.method = (options.method || 'post').toUpperCase();

	// set up xmlttp
	var xmlhttp;
	try {
		xmlhttp = new ActiveXObject('Msxml2.XMLHTTP');
	} catch(e) {
		try {
			xmlhttp = new ActiveXObject('Microsoft.XMLHTTP');
		} catch(e) {
			try {
				xmlhttp = new XMLHttpRequest();
			} catch(e) {
				xmlhttp = false;
			}
		}
	}

	if(!xmlhttp)
		return;

	// stops the call
	this.stop = function() {
		xmlhttp.abort();
	}

	// sends the request
	this.send = function(query) {

		if(!xmlhttp)
			return;

		this.stop();
		options.start();

		xmlhttp.onreadystatechange = function() {
			if(xmlhttp.readyState == 4)
				if(xmlhttp.status == 200)
					options.complete(xmlhttp.responseText.trim());
				else
					options.error();
		}

        // set up url,'cachekiller' is for cachekiller
		var url = options.url + (options.url.indexOf('?') == -1 ? '?' : '&') + (options.method == 'GET' ? query || '' : '') + (options.cache === true ? '' : '&cachekiller=' + $time());

		// open the request
		xmlhttp.open(options.method,url,options.async === false ? false : true);

		// was it a get or post
		if(options.method == 'GET')
			xmlhttp.send(null);
		else {
			xmlhttp.setRequestHeader('Method','POST ' + url + ' HTTP/1.1');
			xmlhttp.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
			xmlhttp.send(query || '');
		}

	}
}


//
// cookies
// thank you Dhryn and quirksmode
//
function Cookie(prefix) {

    this.prefix = prefix || '';

    this.set = function(name,value,days) {
        var date = new Date();
        date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
        document.cookie = setPrefix(name) + '=' + value + '; expires=' + date.toGMTString() + '; path=/';
    }

    this.get = function(name) {
        var ca = document.cookie.split(';'),
            cal = ca.length,
            i, c;
        for(i = 0; i < cal; i++) {
            c = ca[i];
            while(c.charAt(0) == ' ')
                c = c.substring(1,c.length);
            if(c.indexOf(setPrefix(name) + '=') == 0)
                return c.substring((setPrefix(name) + '=').length,c.length);
        }
        return false;
    }

    this.unset = function(name) {
        this.set(name,'',-1);
    }

    function setPrefix(name) {
        return name.indexOf(this.prefix) === 0 ? name : this.prefix + name;
	}
}


//
// color
// thank you leigeber and linuxtopia
//
var Color = {
	toRGB:function(c) {
		c = c.replace(/\#/,'').toString().toLowerCase();
		if(c.length == 3)
			c = c.charAt(0) + c.charAt(0) + c.charAt(1) + c.charAt(1) + c.charAt(2) + c.charAt(2);
		return [ c.substring(0,2).toInt(16), c.substring(2,4).toInt(16), c.substring(4,6).toInt(16) ];
	},
	toHEX:function(c) {
		return this._findHEX(c[0]) + this._findHEX(c[1]) + this._findHEX(c[2]);
	},
	_findHEX:function(c) {
		if(c == undefined || c == null)
			return '00';
		c = c.toInt();
		if(c == 0 || isNaN(c))
			return '00';
		c = Math.max(0,c);
		c = Math.min(c,255);
		c = Math.round(c);
		return '0123456789ABCDEF'.charAt((c - c % 16) / 16) + '0123456789ABCDEF'.charAt(c % 16);
	}
};


//
// JSON encode and decode
// thank you - http://www.openjs.com/scripts/data/json_encode.php (their example doesn't work, had to alter it a bit)
//
var JSON = {
	toString:function(obj) {
		var parts = [], flag = isArray(obj), key, value, str;

		for(key in obj) {
			value = obj[key];
			if(typeof value == 'function')
				continue;
			if(typeof value == 'object')
				parts.push('"' + key + '":' + JSON.toString(value));
			else {
				str = '';
				if(!flag)
					str += '"' + key + '":';
				if(typeof value == 'number')
					str += value;
				else if(typeof value == false)
					str += 'false';
				else if(typeof value == true)
					str += 'true';
				else
					str += '"' + value + '"';
				parts.push(str);
			}
		}

		return flag ? '[' + parts.join(',') + ']' : '{' + parts.join(',') + '}';
	},
	toObject:function(str) {
		return eval('(' + str + ')');
	}
}


//
// Browser detection
// thank you quirksmode
//
var Browser = {
	init:function() {
		this.name = this.searchString(this.dataBrowser) || 'Unknown';
		this.version = this.searchVersion(navigator.userAgent) || this.searchVersion(navigator.appVersion) || 'Unknown';
		this.platform = this.searchString(this.dataOS) || 'Unknown';
	},
	searchString:function(data) {
		var l = data.length,
			dataString,
			dataProp,
			i;
		for(i = 0; i < l; i++) {
			dataString = data[i].string;
			dataProp = data[i].prop;
			this.versionSearchString = data[i].versionSearch || data[i].identity;
			if(dataString) {
				if(dataString.indexOf(data[i].subString) != -1)
					return data[i].identity;
			} else if(dataProp)
				return data[i].identity;
		}
	},
	searchVersion:function(dataString) {
		var index = dataString.indexOf(this.versionSearchString);
		if(index == -1)
			return;
		return dataString.substring(index + this.versionSearchString.length + 1).toFloat();
	},
	dataBrowser:[
		{
			string:navigator.userAgent,
			subString:'Chrome',
			identity:'Chrome'
		},
		{string:navigator.userAgent,
			subString:'OmniWeb',
			versionSearch:'OmniWeb/',
			identity:'OmniWeb'
		},
		{
			string:navigator.vendor,
			subString:'Apple',
			identity:'Safari',
			versionSearch:'Version'
		},
		{
			prop:window.opera,
			identity:'Opera'
		},
		{
			string:navigator.vendor,
			subString:'iCab',
			identity:'iCab'
		},
		{
			string:navigator.vendor,
			subString:'KDE',
			identity:'Konqueror'
		},
		{
			string: navigator.userAgent,
			subString:'Firefox',
			identity:'Firefox'
		},
		{
			string: navigator.vendor,
			subString:'Camino',
			identity:'Camino'
		},
		{
			string:navigator.userAgent,
			subString:'Netscape',
			identity:'Netscape'
		},
		{
			string:navigator.userAgent,
			subString:'MSIE',
			identity:'Explorer',
			versionSearch:'MSIE'
		},
		{
			string:navigator.userAgent,
			subString:'Gecko',
			identity:'Mozilla',
			versionSearch:'rv'
		},
		{
			string:navigator.userAgent,
			subString:'Mozilla',
			identity:'Netscape',
			versionSearch:'Mozilla'
		}
	],
	dataOS:[
		{
			string:navigator.platform,
			subString:'Win',
			identity:'Windows'
		},
		{
			string:navigator.platform,
			subString:'Mac',
			identity:'Mac'
		},
		{
			string:navigator.platform,
			subString:'Linux',
			identity:'Linux'
		}
	]

};


//
// accordion effect
// thank you leigeber
//
function Accordion(id,options) {

	options = options || {};

	// set variables
	var element = $(id),
		headers = element.getElements(options.header || 'dt'),
		sliders = element.getElements(options.slider || 'dd');

	// set sliders
	sliders.each(function(el) {
		el.setStyles({
			height:0,
			display:'none',
			overflow:'hidden'
		});
	});

	// set headers
	headers.each(function(el) {
		el.addEvent('click',slide);
	});

	function slide() {
		var p = this;
		headers.each(function(h, i) {

			el = sliders[i].stopTweens('all');

			if(h == p && el.getStyle('display') == 'none') {
				el.setStyle('display', '');
				moving(el,true);
			} else if(el.getStyle('display') == '' || el.getStyle('display') == 'block')
				moving(el,false);
		});
	}

	function moving(el,flag) {

		// declare some variables
		var l = el.childNodes.length,
			h = 0, i;

		// we don't need to extend the childnodes with GOOP; just get the offsetHeight, if its NOT undefined
		for(i = 0; i < l; i++)
			h += el.childNodes[i].offsetHeight || 0;

		if(flag)
			el.morph({
				height:[0,h],
				opacity:[0,100]
			},{
				duration:300
			});
		else
			el.morph({
				height:[h,0],
				opacity:[100,0]
			},{
				duration:300,
				complete:function() {
					this.setStyle('display','none');
				}
			});
	}

	// automatically show
	if(options.open > -1)
		move(options.open);

	// move
	this.move = function(n) {
		headers[n].doEvent('click');
	}
};


//
// tips
// thanks for reference leigeber
//
function Tip(id,options) {

	options = options || {};
	var element = $(id),
		tip = new Element('div',{
			'class':'tip' + (options._class ? ' tip_' + options._class : ''),
			html:(options.title ? '<strong>' + options.title + '</strong>' : '') + (options.message ? '<p>' + options.message + '</p>' : ''),
			setStyles:{
				opacity:0,
				display:'none',
				position:'absolute',
				zIndex:99
			}
		}).inject(document.body,'top');

	// add events
	element.addEvents({
		mouseover:show,
		mouseout:hide,
		mousemove:follow
	});

	// show the tip
	function show() {
		tip.setStyle('display','block').fade('in',{
			opacity:options.opacity || 85
		});
	}

	// hide the tip
	function hide() {
		tip.fade('out',{
			complete:function() {
				this.setStyle('display','none');
			}
		});
	}

	// follow the cursor
	function follow(e) {

		// get position
		var top = (e.pageY ? e.pageY : e.clientY + document.body.getScroll().top) - tip.getSize().height - 15,
			left = (e.pageX ? e.pageX : e.clientX + document.body.getScroll().left) + 15;

		// fix position so it doesn't go off the page :)
		if(top < 0)
			top = (e.pageY ? e.pageY : e.clientY) + 15;
		if(left + tip.getSize().width > document.body.getSize().width)
			left = (e.pageX ? e.pageX : e.clientX) - tip.getSize().width - 15;

		// set position, finally
		tip.setStyles({
			top:top,
			left:left
		});
	}
}


//
// growl effect
// thanks for reference digitarald and Growl
//
var Growl = {

	growls:[],

	smoke:function(options) {

		options = options || {};

		// make an id for it; variable for height
		var id = 'smoke_' + $time(),
			html = '',
			h = 0;

		// set the height
		this.growls.each(function(el) {
			h += el.getSize().height;
		});

		// set up html for the smoke
		html += options.close ? '<div class="close" style="display:none;" onclick="Growl.close("' + id + '");"></div>' : '';
		html += options.image ? '<div class="image"><img src="' + options.image + '" /></div>' : '';
		html += '<div class="message"><strong>' + (options.title || '') + '</strong><p>' + (options.message || '') + '</p></div>';
		html += '<div style="clear:both;"></div>';

		// create the element and effects and put it in array
		this.growls.push(new Element('div',{
			id:id,
			'class':'smoke' + (options._class ? ' smoke_' + options._class : ''),
			html:html,
			setStyles:{
				opacity:0,
				position:'absolute',
				zIndex:99,
				top:document.body.getScroll().top + h,
				right:0
			}
		}).addEvents({
			mouseover:function() {
				var c = this.getElement('div.close');
				if(c)
					c.setStyle('display','block');
			},
			mouseout:function() {
				var c = this.getElement('div.close');
				if(c)
					c.setStyle('display','none');
			}
		})._set('duration',options.duration || 2000)._set('growl_opacity',options.opacity || 100));

		this.add('smoke');
	},

	bezel:function(options) {

		options = options || {};

		// set up html for the bezel
		var html = (options.image ? '<img src="' + options.image + '" /><br />' : '');
		html += '<div class="message"><strong>' + (options.title || '') + '</strong><p>' + (options.message || '') + '</p></div>';

		// create the element and effects and put it in array
		this.growls.push(new Element('div',{
			'class':'bezel' + (options._class ? ' bezel_' + options._class : ''),
			html:html,
			setStyles:{
				opacity:0,
				position:'absolute',
				zIndex:99,
				height:options.height || 'auto',
				top:document.body.getScroll().top + 300,
				left:'50%'
			}
		})._set('duration',options.duration || 2000)._set('growl_opacity',options.opacity || 100));

		this.add('bezel');
	},

	mv:function(options) {

		options = options || {};

		// set up html for the mv
		var html = (options.image ? '<div class="image"><img src="' + options.image + '" /></div>' : '');
		html += '<div class="message"><strong>' + (options.title || '') + '</strong><p>' + (options.message || '') + '</p></div>';
		html += '<div style="clear:both;"></div>';

		// create the element and effects and put it in array
		this.growls.push(new Element('div',{
			'class':'mv' + (options._class ? ' mv_' + options._class : ''),
			html:html,
			setStyles:{
				opacity:options.opacity || 100,
				position:'absolute',
				zIndex:99,
				width:document.body.getSize().real_width,
				top:0,
				left:0
			}
		})._set('duration',options.duration || 2000));

		this.add('mv');
	},

	doScroll:function() {
		var h = 0,
			m = 0;
		Growl.growls.each(function(el,i) {
			m = el.getStyle('margin');
			h += (i > 0 ? el.getSize().height + (m != '' ? m.toInt() : 0) : 0);
			el.setStyle((el.hasClass('bezel') ? 'margin-' : '') + 'top',document.body.getScroll().top + h);
		});
	},

	addScroll:function() {
		window.addEvent('scroll',this.doScroll);
	},

	removeScroll:function() {
		if(this.growls.length > 0)
			this.growls.shift().destroy();
		else
			window.removeEvent('scroll',this.doScroll);
	},

	add:function(what) {

		// add scroll event
		if(this.growls.length == 0)
			return;
		else if(this.growls.length == 1)
			this.addScroll();

		// acts as parent; get the first element, then put it back
		var p = this,
			el = this.growls.pop();
		this.growls.push(el);

		// only smoke can have more than one at a time
		if(what == 'bezel') {
			if($$("div.bezel") != null)
				return;
		}else if(what == 'mv') {
			if($$("div.mv") != null)
				return;
		}

		// place in body at the top
		el.inject(document.body,'top');

		if(what == 'mv') {

			el.setStyle('visibility','hidden').hide({
				complete:function() {
					el.setStyle('visibility','visible').slide('in',{
						complete:function() {
							$timeout(el._get('duration'),function() {
								el.slide('out',{
									complete:function() {
										p.removeScroll();
										p.add(what);
									}
								});
							});
						}
					});
				}
			});

		}else {

			el.fade('in',{
				opacity:el._get('growl_opacity'),
				complete:function() {
					$timeout(el._get('duration'),function() {
						el.fade('out',{
							opacity:el._get('growl_opacity'),
							complete:function() {
								p.removeScroll();
								if(what == 'bezel')
									p.add(what);
							}
						});
					});
				}
			});

			// makes it centered
			if(what == 'bezel')
				el.setStyle('margin-left',-(el.getSize().width / 2));
		}

	},

	close:function(id) {
		var p = this;
		this.growls.each(function(el,i) {
			if(el.get('id') == id)
				el.fade('out',{
					complete:function() {
						p.growls.splice(i,1);
						this.destroy();
					}
				});
		});
	}
};


//
// Table Sorter
// HUGE THANKS to leigeber - this is completely rewritten from his famous TINY Table to fit with GOOP... no credit here
//
function TableSorter(id,options) {

	options = options || {};

	var element = $(id),
		thead = element.getElement('thead',[]),
		tbody = element.getElement('tbody',[]),
		rows = tbody.getElements('tr',['styles']),
		rows_length = rows.length,
		cells = [],
		col = -1,
		order,
		pp = options.perpage || 20,
		pages_max = 1,
		pages_current = 1,
		i, el;

	// set up thead cells
	for(i = 0; i < thead.getElement('tr').cells.length; i++) {
		el = $(thead.getElement('tr').cells[i],['events','dimensions']);
		if(!el.hasClass('nosort'))
			el.addEvent('click',sort);
	};

	// make the cell array with objects
	for(i = 0; i < rows_length; i++)
		cells[i] = {};

	// start and complete functions
	element.started = options.start || function() {};
	element.completed = options.complete || function() {};

	// auto sort
	if(options.sort > -1)
		thead.getElement('tr').cells[options.sort].doEvent('click');
	else {
		if(options.paginate)
			perpage(pp);
	}

	// search ability?
	if(options.search) {

		var tr = new Element('tr',{'class':'tablesorter_search'},[]);
		tr.inject(thead);

		for(i = 0; i < thead.getElement('tr',false).cells.length; i++) {
			el = new Element('td',{'class':'tablesorter_search'},[]);
			if(options.search.indexOf(i) > -1)
				new Element('input',{
					type:'text',
					setStyles:{
						width:thead.getElement('tr',false).cells[i].getSize().width
					},
					addEvents:{
						keyup:search
					}
				}).inject(el);
			el.inject(tr);
		}
	}

	// sort the table
	function sort() {

		element.started();

		var tr = tbody.getElement('tr.tablesorter_noresults'),
			tb;
		if(tr)
			tr.destroy();

		// remove all header class names
		for(i = 0; i < thead.getElements('tr',false)[options.search ? 1 : 0].cells.length; i++)
			thead.getElements('tr',false)[options.search ? 1 : 0].cells[i].removeClass('asc').removeClass('desc');

		if(col == this.cellIndex) {
			cells.reverse();
			order = (order == 'asc') ? 'desc' : 'asc';
			this.set('class',order);
		} else {
			col = this.cellIndex;
			rows.each(function(el,i) {
				var v = el.setStyle('display','').cells[col];
				while(v.hasChildNodes()) v = v.firstChild;
				cells[i].v = (v.nodeValue || '').toLowerCase();
				cells[i].n = i;
			});
			cells.sort(compare);
			if(options.order) {
				order = options.order;
				options.order = -1;
				if(order == 'desc') cells.reverse();
			} else
				order = 'asc';
			this.set('class',order);
		}

		// create a new tbody and replace the old
		tb = new Element('tbody',{},[]);
		cells.each(function(el,i) {
			tb.adopt(rows[el.n].set('class', i % 2 ? 'even' : 'odd'));
		});
		tb.replaces(tbody);
		tbody = tb;

		// show the current page we are on
		if(options.paginate)
			perpage(pp);

		element.completed();
	}

	// comapre values
	function compare(f,c) {
		var g, h, i;
		f = g = f.v,
		c = h = c.v;
        i = f.replace(/(\$|\,)/g, '').toFloat(),
		n = c.replace(/(\$|\,)/g, '').toFloat();
        if(!isNaN(i) && !isNaN(n)) g = i, h = n;
		i = Date.parse(f), n = Date.parse(c);
		if(!isNaN(i) && !isNaN(n)) g = i, h = n;
        return g > h ? 1 : (g < h ? -1 : 0);
    }

	function search() {

		var p = this,
			tr = tbody.getElement('tr.tablesorter_noresults'),
			c = 0,
			w = 1,
			v, f;

		if(this.value == '') {
			if(tr)
				tr.destroy();
			perpage(pp);
		} else {

			// find the cell value we are looking in
			tbody.getElements('tr',['styles']).each(function(el) {
				if(!el.hasClass('tablesorter_noresults')) {

					v = el.cells[p.parentNode.cellIndex];
					while(v.hasChildNodes())
						v = v.firstChild;
					v = (v.nodeValue || '').toLowerCase();

					f = 0;
					if(v.indexOf(p.value) > -1 && c < pp) f = 1, c++;

					el.setStyle('display',f ? '' : 'none');
					if(f) w++, el.set('class',w % 2 ? 'even' : 'odd');
				}
			});

			// no found results
			if(c > 0) {
				if(tr)
					tr.destroy();
			} else {
				if(tr)
					return;
				tr = new Element('tr',{'class':'tablesorter_noresults'},[]);
				new Element('td',{
					colspan:thead.getElement('tr').cells.length,
					align:'center',
					html:'No Results'
				}).inject(tr.inject(tbody));
			}

		}

	}

	function move(d,f) {
		var s = d == 1 ? (f ? pages_max : pages_current + 1) : (f ? 1 : pages_current - 1);
		if(s > pages_max || s <= 0)
			return;
		pages_current = s;
		page((s - 1) * pp);
		if(options._ids[0])
			$(options._ids[0]).set('html',pages_current);
		element.completed();
	}

	function perpage(n) {
		pp = n.toInt();
		pages_max = Math.ceil(rows_length / pp);
		move(-1,true); // show the beginning
		if(options._ids[1])
			$(options._ids[1]).set('html',pages_max);
		element.completed();
	}

	function page(n) {
		var m = n + pp;
		tbody.getElements('tr',['styles']).each(function(el,i) {
			el.setStyle('display',i >= n && i < m ? '' : 'none').set('class',i % 2 ? options.even || 'even' : options.odd || 'odd');
		});
		element.completed();
	}

	// save for prototypes/methods
	this.move = move;
	this.perpage = perpage;
	this.page = page;
}


//
// tab slider
// thank you for the idea creative pony and panic
//
function SlideTabs(id,options) {

	options = options || {};
	options.slide = options.slide == undefined ? true : options.slide;

	var element = $(id),
		tabs = element.getElements(options.tabs || 'li.tab'),
		pans = element.getElements(options.pans || 'div.pan'),
		count = tabs.length,
		pans_width = pans[0].getSize().width,
		wrapper_width = pans_width * count,
		current_pan_index,
		container,
		wrapper;

	// set tabs and pans
	setTabsPans();

	// make a wrapper
	wrapper = new Element('div',{
		id:'slidetab_wrapper',
		setStyles:{
			marginLeft:0,
			width:wrapper_width
		}
	}).wraps(pans);

	// make a container
	container = new Element('div',{
		id:'slidetab_container',
		setStyles:{
			overflow:'hidden',
			width:pans_width
		}
	}).wraps(wrapper);

	// auto show?
	if(options.show > -1) {
		var h = options.slide;
		options.slide = false;
		tabs[options.show].doEvent('click');
		options.slide = h;
	} else {
		tabs[0].doEvent('click');
	}

	// add event and fire it
	window.addEvent('resize',fixPans).doEvent('resize');

	// slide the wrapper
	function slide() {

		// change the class
		var p = this,
			next;
		tabs.each(function(el) {
			if(el == p)
				el.addClass('current');
			else
				el.removeClass('current');
		});

		next = -(this._get('slidetab_index') * pans_width);
		current_pan_index = this._get('slidetab_index');

		wrapper.tween('margin-left',wrapper.getStyle('margin-left').toInt(),next,{
			duration:options.slide ? 500 : 1
		});
	}

	function setTabsPans() {
		// set the header tab index and click event
		tabs.each(function(el,i) {
			el._set('slidetab_index',i).removeEvent('click',slide).addEvent('click',slide);
		});

		// set the width for the pans
		pans.each(function(el) {
			el.setStyles({
				width:pans_width,
				'float':'left'
			});
		});
	}

	function setWidths() {
		// get new width
		wrapper_width = pans_width * count;

		// reset the width for the pans
		pans.each(function(el) {
			el.setStyle('width',pans_width);
		});

		// adjust the wrapper and container width
		container.setStyle('width',pans_width);
		wrapper.setStyle('width',wrapper_width).setStyle('margin-left',-(current_pan_index || 0 * pans_width));
	}

	// fix the pans when the window is resized
	function fixPans() {
		// gets new width
		pans_width = container.setStyle('width','100%').getSize().width;
		// resets the width
		setWidths();
	}

	// add a tab
	this.addTab = function(obj) {

		// set the object
		obj = obj || {};
		obj.pos = obj.pos === undefined ? 'end' : obj.pos;
		var pos = obj.pos === 'end' ? tabs.length - 1 : obj.pos,
			where = obj.pos === 'end' ? 'after' : 'before',
			tab = $(tabs[0].cloneNode(true)).set('html', obj.title || 'New Tab'), // get clones, then insert them
			pan = $(pans[0].cloneNode(true)).set('html', obj.content || 'New Content');
		tab.inject(tabs[pos], where);
		pan.inject(pans[pos], where);

		pos += where === 'after' ? 1 : 0;
		tabs.splice(pos, 0, tab);
		pans.splice(pos, 0, pan);

		// increment count
		count++;

		// set the tabs and pans again
		fixPans();
		setTabsPans();

		// show the new tab
		tabs[pos].doEvent('click');
	}

	// remove a tab
	this.removeTab = function(n) {
		if(!tabs[n])
			return;
		tabs[n].destroy(); // destroy
		pans[n].destroy();
		tabs.splice(n, 1); // remove
		pans.splice(n, 1);
		count--; // minus one
		fixPans(); // fix
		setTabsPans();
		if(n - 1 > -1)
			tabs[--n].doEvent('click'); // show the pan before the one removed
	}

	// removes the current tab
	this.removeCurrent = function() {
		this.removeTab(current_pan_index);
	}
}


//
// autocomplete
//
function AutoComplete(id,options) {

	options = options || {};
	if(!options.path)
		return;

	options.limit = options.limit || 20;

	var element = $(id),
		list = [],
		selected = -1,
		ajax = new Ajax({
			url:options.path,
			complete:didSearch
		}),
		ul = new Element('ul',{
			'class':'autocomplete',
			setStyles:{
				display:'none',
				position:'absolute',
				zIndex:99
			}
		}).inject(document.body,'top');

	// add event to element
	element.addEvents({
		keyup:doSearch,
		blur:hideSearch
	});

	function hideSearch() {
		$timeout(500,function() {
			ul.setStyle('display','none');
		});
	}

	function doSearch(e) {

		// move the list
		ul.setStyles({
			top:element.getPosition().top + element.getSize().height,
			left:element.getPosition().left
		});

		// determine what key was pressed
		switch(new Event(e).code) {
			case 37: // left arrow
				return;
			case 39: // right arrow
				return;
			case 38: // up arrow
				selected = selected > 0 ? selected - 1 : 0;
				showSelected();
				break;
			case 40: // down arrow
				selected = selected < list.length - 1 ? selected + 1 : list.length - 1;
				showSelected();
				break;
			case 13: // enter
				if(list[selected])
					list[selected].doEvent('click');
				break;
			case 27: // escape
				ul.setStyle('display','none');
				break;
			default:
				list = [], selected = -1;
				ajax.send('value=' + this.value);
				break;
		}
	}

	function didSearch(html) {

		// empty all children inside of ul
		ul.empty();

		var results = JSON.toObject(html),
			c = 0,
			i;

		for(i in results) {

			list[c++] = new Element('li',{
				html:i,
				setStyles:{
					cursor:'pointer'
				},
				addEvents:{
					click:function() {
						window.location.href = this._get('autocomplete_click');
					},
					mouseover:function() {
						selected = this._get('autocomplete_c');
						showSelected();
					}
				}
			},['styles','events'])._set('autocomplete_c',c - 1)._set('autocomplete_click',results[i]).inject(ul);

			// limit
			if(c >= options.limit)
				break;
		}

		ul.setStyle('display',c == 0 ? 'none' : '');

	}

	function showSelected() {
		list.each(function(el,i) {
			if(selected == i)
				el.addClass('over');
			else
				el.removeClass('over');
		});
	}

}


//
// Validate
// idea from leigeber
//
var Validate = {

	move:true, // true to move window to error, false otherwise
	goaway:true, // do you want the validate warning to go away?
	position:'right', // appears to the right, bottom, or top of the error
	offset_x:10,
	offset_y:5,
	duration:2500, // how long before it fades out

	// legal characers only
	legal:function(input,msg) {
		var value, flag;
		value = (typeof input == 'object' ? input.value : input).trim();
		flag = (value != '' ? !(/\W/).test(value) : false);
		if(!flag && typeof input == 'object')
			this.alert(input,msg || 'Invalid Characters');
		return flag;
	},

	// validate email - sample@sample.com
	email:function(input,msg) {
		var value, flag;
		value = (typeof input == 'object' ? input.value : input).trim();
		flag = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/.test(value);
		if(!flag && typeof input == 'object')
			this.alert(input,msg || 'Invalid Email');
		return flag;
	},

	// phone - (123) 456-7890
	phone:function(input,msg) {
		var value, flag;
		value = (typeof input == 'object' ? input.value : input).trim();
		flag = /^\([1-9]\d{2}\)\s?\d{3}\-\d{4}$/.test(value);
		if(!flag && typeof input == 'object')
			this.alert(input,msg || 'Invalid Phone');
		return flag;
	},

	// number
	number:function(input,msg) {
		var value, flag;
		value = (typeof input == 'object' ? input.value : input).trim();
		flag = /(^-?\d\d*\.\d*$)|(^-?\d\d*$)|(^-?\.\d\d*$)/.test(value);
		if(!flag && typeof input == 'object')
			this.alert(input,msg || 'Invalid Number');
		return flag;
	},

	// letters only
	letters:function(input,msg) {
		var value, flag;
		value = (typeof input == 'object' ? input.value : input).trim();
		flag = /^[a-zA-Z]+$/.test(value);
		if(!flag && typeof input == 'object')
			this.alert(input,msg || 'Characters Only');
		return flag;
	},

	// zip - 12345 or 12345-1234
	zip:function(input,msg) {
		var value, flag;
		value = (typeof input == 'object' ? input.value : input).trim();
		flag = /\d{5}(-\d{4})?/.test(value);
		if(!flag && typeof input == 'object')
			this.alert(input,msg || 'Invalid Zip');
		return flag;
	},

	// date - MM/DD/YYYY
	date:function(input,msg) {
		var value, flag;
		value = (typeof input == 'object' ? input.value : input).trim();
		flag = /^([1-9]|0[1-9]|1[012])\D([1-9]|0[1-9]|[12][0-9]|3[01])\D(19[0-9][0-9]|20[0-9][0-9])$/.test(value);
		if(!flag && typeof input == 'object')
			this.alert(input,msg || 'Invalid Date');
		return flag;
	},

	// url - http://www.sample.com or www.sample.com
	url:function(input,msg) {
		var value, flag;
		value = (typeof input == 'object' ? input.value : input).trim();
		flag = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/.test(value);
		if(!flag && typeof input == 'object')
			this.alert(input,msg || 'Invalid URL');
		return flag;
	},

	// extension - default: jpeg, jpg, gif, png, swf ... pass an array to match your own
	ext:function(input,msg,ex) {
		var value, flag;
		value = (typeof input == 'object' ? input.value : input).trim();
		value = value.substring(value.lastIndexOf('.') + 1).toLowerCase();
		flag = false;
		(ex || ['jpeg','jpg','gif','png','swf']).each(function(e) {
			if(e == value)
				flag = true;
		});
		if(!flag && typeof input == 'object')
			this.alert(input,msg || 'Invalid Extension Type');
		return flag;
	},

	// determines if the input field is empty
	empty:function(input,msg) {
		var value, flag, i;
		if(typeof input == 'object') {
			if(input.tagName == 'SELECT')
				value = input.options[input.selectedIndex].value.trim();
			else if(input.tagName == 'INPUT' || input.tagName == 'TEXTAREA')
				value = input.value.trim();
			else if(input[0] && input.tagName == undefined) {
				value = ''; // lets assume its not checked
				for(i = 0; i < input.length; i++) {
					if(input[i].checked)
						value = 'checked'; // value is now set to something
				}
				input = input[0]; // for the error message, if any
			}
		} else
			value = input.trim();
		flag = value != '';
		if(!flag && typeof input == 'object')
			this.alert(input,msg || 'Can not be empty');
		return !flag;
	},

	// alerts the warning
	alert:function(element,msg) {

		if(!element || !msg || typeof element != 'object')
			return;

		// make local variables
		var p = this,
			pos,
			ox,
			oy,
			div,
			el;

		// extend the element first, then we can get the dimensions
		GOOP.extend(element,['dimensions']);
		pos = element.getCoordinates();

		// find offset for x and y (or left and top)
		if(this.position == 'right')
			ox = pos.left + pos.width + this.offset_x,
			oy = pos.top - this.offset_y;
		else if(this.position == 'left')
			ox = pos.left,
			oy = pos.top - this.offset_y; // will be set later
		else if(this.position == 'top')
			ox = pos.left,
			oy = pos.top; // will be set later
		else if(this.position == 'bottom')
			ox = pos.left,
			oy = pos.bottom + this.offset_y;

		// wrapper
		div = new Element('div', {
			html:msg,
			'class':'validate',
			setStyles:{
				position:'absolute',
				zIndex:100,
				opacity:0,
				top:oy,
				left:ox
			}
		}).inject(document.body,'top').fade('in',{
			complete:function() {
				if(!p.goaway) return;
				el = this;
				$timeout(p.duration,function() {
					el.fade('out',{
						complete:function() {
							this.destroy();
						}
					});
				});
			}
		});

		// fix position IF it was set to top OR left... we do it here because padding and margin has yet to be determined
		if(this.position == 'top')
			div.setStyle('top',div.getStyle('top').toInt() - div.getSize().height - this.offset_y);
		else if(this.position == 'left')
			div.setStyle('left',div.getStyle('left').toInt() - div.getSize().width - this.offset_x);

		// move to element
		if(this.move)
			window.scrollTo(pos.left, pos.top);
	}

};


//
// date picker
// reference from jszen.blogspot.com
//
var DatePicker = {

	element:null,

	//
	picked:function(year,month,day) {
		if(this.element == null)
			return;
		month++; // so it reads easier
		this.element.value = year + '-' + (month < 10 ? '0' : '') + month + '-' + (day < 10 ? '0' : '') + day;
		this.close();
	},

	//
	changeDate:function(y,m) {
		if(m < 0)
			y - 1,
			m = 11;
		if(m > 11)
			y + 1,
			m = 0;
		this.close();
		this.open(this.element,{year:y,month:m});
	},

	// open the datepicker
	open:function(element,options) {

		this.element = element || this.element;
		if(!this.element)
			return;
		if($('datepicker') != null)
			return;

		options = options || {};

		var date = new Date(),
			month = (options.month != undefined ? options.month : date.getMonth()),
			year = (options.year != undefined ? options.year : date.getFullYear()),
			days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
			months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
			days_in_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
			first_day = new Date(year, month, 1),
			start_day = first_day.getDay(),
			month_length = days_in_month[month],
			html,
			day = 1,
			i, j;

		if(month == 1)
			if((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
				month_length++;
		//
		html = '<table class="datepicker">';

		// close
		html += '<tr><td align="right" colspan="7"><a href="javascript:DatePicker.close();" class="close">Close</a></td></tr>';

		// month and year
		html += '<tr><th colspan="7">' + months[month] + ' ' + year + '</th></tr>';

		// options
		html += '<tr class="datepicker_options">';
		html += '<td align="center"><a href="javascript:DatePicker.changeDate(' + (year - 1) + ',' + month + ');"><<</a></td>';
		html += '<td align="center"><a href="javascript:DatePicker.changeDate(' + year + ',' + (month - 1) + ');"><</a></td>';
		html += '<td align="center" colspan="3"><a href="javascript:DatePicker.changeDate(' + date.getFullYear() + ',' + date.getMonth() + ');">Today</a></td>';
		html += '<td align="center"><a href="javascript:DatePicker.changeDate('+ year + ',' + (month + 1) + ');">></a></td>';
		html += '<td align="center"><a href="javascript:DatePicker.changeDate(' + (year + 1) + ',' + month + ');">>></a></td>';
		html += '</tr>';

		// days of week
		html += '<tr class="datepicker_header">';
		for(i = 0; i < 7; i++)
			html += '<td>' + days[i] + '</td>';
		html += '</tr>';

		// days of month
		html += '<tr>';
		for(i = 0; i < 9; i++) {
			for(j = 0; j < 7; j++) {
				html += '<td class="day" onclick="DatePicker.picked(' + year + ',' + month + ',' + day + ');">';
				if(day <= month_length && (i > 0 || j >= start_day)) {

					if(year == date.getFullYear() && month == date.getMonth() && day == date.getDate())
						html += '<strong>' + day + '</strong>';
					else
						html += day;
					day++;
				}
				html += '</td>';
			}
			if(day > month_length)
				break;
			else
				html += '</tr><tr>';
		}

		// done
		html += '</tr></table>';

		// extend the element, then we can get the dimensions
		GOOP.extend(this.element,['dimensions']);
		var pos = this.element.getCoordinates();

		// wrapper
		new Element('div',{
			html:html,
			id:'datepicker',
			setStyles:{
				position:'absolute',
				zIndex:99,
				top:pos.top,
				left:pos.left + pos.width + 10
			}
		}).inject(document.body,'top');
	},

	// closes the datepicker
	close:function() {
		var dp = $('datepicker');
		if(dp != null)
			dp.destroy();
	}

};

//
// rounder - makes a rounded
// thank you Steven Wittens
//
function Rounder(id,options) {

	options = options || {};
	options.bg = options.bg || '000000';
	options.fg = options.fg || 'eeeeee';
	options.radius = options.radius || 8;
	options.size = options.size || 1;

	var element = $(id),
		html = element.get('html'); // get html and erase it
	element.set('html','');

	// round the element
	if(options.top === true || options.top == undefined)
		_round(element,options,'top');
	rounder_content(element,options);
	if(options.bottom === true || options.bottom == undefined)
		_round(element,options,'bottom');

	// put the html back in it, and fix the orginial element
	element.setStyles({
		border:'none',
		backgroundColor:'transparent'
	}).getElement('div.rounder_content').set('html',html);

	function sqr(n) {
		return n * n;
	}

	function _round(el,options,where) {

		var div = new Element('div',{},[]),
			begin = where == 'top' ? 1 : options.radius,
			end = where == 'top' ? options.radius : 1,
			increment = where == 'top' ? 1 : -1,
			arc, i;

		for(i = begin; (where == 'top' ? i <= end : i >= end); i+=increment) {

			arc = -(Math.sqrt(1 - sqr(1 - i / options.radius)) * options.radius);

			div.adopt(new Element('div', {
				setStyles:{
					margin:'0px ' + (arc + options.radius) + 'px',
					padding:0,
					height:i == end ? Math.ceil(options.size / 2) : 1,
					backgroundColor:'#' + (i == 1 ? options.bg : options.fg),
					borderLeft:options.size + 'px solid #' + options.bg,
					borderRight:options.size + 'px solid #' + options.bg
				}
			}));
		}

		div.inject(el,where);
	}

	function rounder_content(el,options) {

		el.adopt(new Element('div',{
			'class':'rounder_content',
			setStyles:{
				backgroundColor:(options.fg != 'transparent' ? '#' : '') + options.fg,
				borderTop:options.top === false ? options.size + 'px solid #' + options.bg : 'none',
				borderRight:options.size + 'px solid #' + options.bg,
				borderBottom:options.bottom === false ? options.size + 'px solid #' + options.bg : 'none',
				borderLeft:options.size + 'px solid #' + options.bg
			}
		}));

	}

};


//
// drag and drop
// thank you Luke Breuer - http://luke.breuer.com/tutorial/javascript-drag-and-drop-tutorial.aspx
//
function DragDrop(id,options) {

	options = options || {};
	options.revert = options.revert === false ? false : true;
	options.x = options.x === false ? false : true;
	options.y = options.y === false ? false : true;
	options.disable = options.disable === false ? false : true;
	options.sort = options.sort || false;

	var element = $(id),
		handle = options.handle ? element.getElement(options.handle) : null,
		constrain = options.constrain || options.sort ? $(options.constrain || element).getCoordinates() : null,
		sx = 0,
		sy = 0,
		ox = 0,
		oy = 0,
		z = element.getStyle('z-index'),
		target = null,
		drag = false;

	// if constrain is not null, we need to make the boundry object
	if(constrain) {
		var c = element.getCoordinates(),
			boundry = {};
		boundry.minx = -(c.left - constrain.left);
		boundry.maxx = constrain.right - c.left - c.width;
		boundry.miny = -(c.top - constrain.top);
		boundry.maxy = constrain.bottom - c.top - c.height;
		if(boundry.maxx < 0) boundry.maxx = 0;
		if(boundry.maxy < 0) boundry.maxy = 0;
	}

	// if sorting, create some more variables
	if(options.sort)
		var list = options.list || null,
			is_tr = element.tagName == 'TR',
			sort_dy,
			sort_y;

	// can't be a static element
	element.setStyle('position','relative');

	// add the functions
	element.started = options.start || function() {};
	element.moved = options.move || function() {};
	element.completed = options.complete || function() {};

	document.body.addEvent('mousedown', mousedown);
	document.body.addEvent('mouseup', mouseup);

	// if strict, disable the text selection - this is for IE
	if(options.disable || options.sort) {
		var t = handle ? handle : element;
		t.onselectstart = function() {return false;}
		t.onmousedown = function() {return false;}
		t.ondragstart = function() {return false;}
	}

	function mousedown(e) {

		target = e.target ? e.target : e.srcElement;

		// let input fields get higher priority
		if(/(input|select|textarea|a)/i.test(target.tagName)) {
			target.focus();
			return;
		}

		// it will grab the td/cell, so we have to loop backwords to get the tr
		if(is_tr) {
			while(target != element && target) // KEEP the '&& target' for IE's sake
				target = target.parentNode;
		}

		// not the element we want to move
		if((target !== handle && handle != null) || (target !== element && handle == null))
			return;

		// check the click
		if(e.button == 1 && window.event != null || e.button == 0) {

			// dragging
			drag = true;

			// grab the mouse position
			e = new Event(e);
			sx = e.page.x;
			sy = e.page.y;

			// grab the clicked elements position
			ox = element.getStyle('left').toInt();
			oy = element.getStyle('top').toInt();
			ox = isNaN(ox) ? 0 : ox;
			oy = isNaN(oy) ? 0 : oy;

			// bring to the front, and start the function
			element.setStyle('z-index',99).started();

			// add event
			document.body.addEvent('mousemove', mousemove);
		}
	}

	function mousemove(e) {

		e = new Event(e);
		var x = ox + e.page.x - sx,
			y = oy + e.page.y - sy;

		element.setStyles({
			left:options.x ? x : '',
			top:options.y ? y : ''
		});

		// constrain
		if(constrain) {
			if(x < boundry.minx)
				element.setStyle('left',boundry.minx);
			if(x > boundry.maxx)
				element.setStyle('left',boundry.maxx);
			if(y < boundry.miny)
				element.setStyle('top',boundry.miny);
			if(y > boundry.maxy)
				element.setStyle('top',boundry.maxy);
		}

		// sorting
		if(options.sort) {

			sort_dy = sort_y > e.page.y;
			sort_y = e.page.y;

			// the element to swap with
			var el, c;

			list.each(function(li) {
				if(li !== element && !el) {
					c = li.getCoordinates();
					if(e.page.x > c.left && e.page.x < c.right && sort_y > c.top && sort_y < c.bottom)
						el = li;
				}
			});

			if(el) {
				sx = e.page.x;
				sy = e.page.y;
				if(sort_dy)
					element.inject(el,'before');
				else
					element.inject(el,'after');
			}
		}

		// do function
		element.moved();
	}

	function mouseup() {

		// stop moving the element
		if(!drag) return;

		// not dragging anymore
		drag = false;

		// remove the event
		document.body.removeEvent('mousemove',mousemove);

		// completed
		element.completed();

		// do this at end
		if(options.revert)
			element.setStyles({
				top:0,
				left:0,
				zIndex:z
			});
		else
			element.setStyle('z-index',z);
	}

}


//
// sortable list
//
function Sortables(id,options) {

	options = options || {};

	var element = $(id),
		list = getList();

	// make it draggable
	list.each(function(li) {
		if(!li.hasClass('nosort'))
			new DragDrop(li,{
				sort:true,
				revert:true,
				constrain:element,
				list:list, // pass the list to compare with when sorting
				start:options.start,
				move:options.move,
				complete:options.complete
			});
	});

	function getList() {
		var l = element.getElements(options.list || 'li');

		// if TR for sorting, we need to get TR's in the tbody, we don't want to sort the thead and tfoot TR's
		if(l[0].tagName == 'TR')
			l = element.getElement('tbody').getElements(options.list);

		return l;
	}

	this.getList = getList;
}


//
// slider
//
function Slider(scroller,knob,options) {

	options = options || {};
	options.min = options.min || 0;
	options.max = options.max || 0;
	options.preset = options.preset || false;

	new DragDrop(knob,{
		constrain:scroller,
		revert:false,
		move:function() {
			this.setStyle('margin-left', this.getStyle('left').toInt());
		}
	});

}



//
// reflect
// reflections on images - big thanks to reflection.js 2.0 - http://cow.neondragon.net/stuff/reflection/
//
function Reflect(id,options) {

	options = options || {};
	options.height = options.height || .5;
	options.opacity = (options.opacity || 50) * .01;

	// to save on errors
	if(!GOOP.dom || !GOOP.load) {
		recheck(50);
		return;
	}

	var element = (this == window || id == window ? $$('img.reflect') : $(id));

	if(element == null)
		return;

	if(isArray(element))
		element.each(function(el) {
			add(el);
		});
	else
		add(element);

	// add the reflection
	function add(el) {

		// remove old reflection first
		remove(el);

		if(!el.tagName) {
			recheck(50);
			return;
		}

		var h = el.getSize().height,
			w = el.getSize().width,
			rh = Math.floor(h * options.height),
			dh = Math.floor(h * (1 + options.height));

		if(Browser.name == 'Explorer') {

			var a, d, reflection;

			// fix hyberlinks
			if(el.parentNode.tagName == 'A')
				a = new Element('a',{'href':el.parentNode.href},[]);
			else
				d = new Element('div',{},[]);

			reflection = new Element('img',{
				src:el.get('src'),
				setStyles:{
					width:w,
					height:h,
					display:'block',
					marginBottom:-(h - rh),
					filter:'flipv progid:DXImageTransform.Microsoft.Alpha(opacity=' + (options.opacity * 100) + ', style=1, finishOpacity=0, startx=0, starty=0, finishx=0, finishy=' + (options.height * 100) + ')'
				}
			});

			reflection.inject((a || d).wraps(el));

		} else {

			var context,
				gradient,
				canvas = new Element('canvas',{
					'class':el.get('class'),
					width:w,
					height:h * .5
				});

			if(!canvas.getContext)
				return;

			context = canvas.getContext('2d');
			context.save();
			context.translate(0,h-1);
			context.scale(1,-1);
			context.drawImage(el, 0, 0, w, h);
			context.restore();
			context.globalCompositeOperation = 'destination-out';

			gradient = context.createLinearGradient(0, 0, 0, rh);
			gradient.addColorStop(1, 'rgba(255, 255, 255, 1.0)');
			gradient.addColorStop(0, 'rgba(255, 255, 255, ' + (1 - options.opacity) + ')');

			context.fillStyle = gradient;
			context.rect(0, 0, w, rh * 2);
			context.fill();

			canvas.inject(new Element('div',{
				setStyles:{
					width:w
				}
			},['styles']).wraps(el.setStyle('vertical-align','bottom')));

		}
	}

	function remove(el) {
		if(!el.hasClass('reflect'))
			return;
		var p = GOOP.extend(el.parentNode,[]),
			c = p.getElement('canvas');
		if(c) {
			c.destroy();
			el.replaces(p);
		}
	}

	function recheck(m) {
		$timeout(m, function() {Reflect(window);});
	}
}


//
// Overlay
// thanks leigeber for the idea
//

var Overlay = {
	divs:[],
	show:function(content,width,height,ajax) {

		// acts as parent
		var p = this,
		    c = $('overlay_content'),
		    m;

		// create the elements
		if(!c) {

			c = new Element('div', {
				id:'overlay_content',
				setStyles:{
					opacity:0,
					position:'absolute',
					zIndex:99,
					width:width ? width : 'auto',
					height:height ? height : 'auto'
				}
			});
			m = new Element('div', {
				id:'overlay_mask',
				setStyles:{
					opacity:0,
					position:'absolute',
					zIndex:99
				},
				addEvents:{
					click:function() {
						p.hide();
					}
				}
			});

			this.divs.push(
				c.inject(document.body,'top').tween('opacity', 0, 100),
				m.inject(document.body,'top').tween('opacity', 0, 70)
			);

			window.addEvent('resize',this.resize);
		}

		// set the content
		c.set('html',content);

		if(!ajax)
			window.doEvent('resize');
		else
			new Ajax({
				url:ajax.url,
				complete:function(html) {
					c.set('html',html);
					window.doEvent('resize');
				}
			}).send(ajax.variables || '');
	},
	hide:function() {
		Overlay.divs.each(function(el) {
			el.tween('opacity', el.getStyle('opacity'), 0, {
				complete:function() {
					this.destroy();
				}
			});
		});
		Overlay.divs = [];
		window.removeEvent('resize',Overlay.resize);
	},
	resize:function() {

		var c = Overlay.divs[0],
			m = Overlay.divs[1],
			b = document.body.getSize();

		c.setStyles({
			top:b.height / 2 - c.getSize().height / 2,
			left:b.width / 2 - c.getSize().width / 2
		});

		m.setStyles({
			width:b.real_width,
			height:b.real_height
		});
	}
};


//
// allow tabs
// thanks UvumiTools TextArea
//
function AllowTabs(id) {

	$(id).addEvent('keydown', function(e) {

		e = new Event(e);

		// tab
		if(e.code == 9) {
			e.event.preventDefault();

			if(Browser.name == 'Explorer') {
				var range = document.selection.createRange();
				range.text = '\t';
			} else {
				var start = this.selectionStart, end = this.selectionEnd, value = this.value;
				this.value = this.value.substring(0, start) + '\t' + this.value.substring(end, this.value.length);
				start++;
				this.setSelectionRange(start, start);
			}
		}

	});

}


//
// extended prototypes
// thank you MooTools
//

// array
Array.prototype.each = function(ftn) {
	for(var i = 0, l = this.length; i < l; i++)
		ftn(this[i],i);
	return this;
}
Array.prototype.indexOf = function(search) {
	for(var i = 0, l = this.length; i < l; i++)
		if(this[i] == search)
			return i;
	return -1;
}
Array.prototype.lastIndexOf = function(search) {
	for(var i = this.length; i > -1; i--)
		if(this[i] == search)
			return i;
	return -1;
}
Array.prototype.shuffle = function() {
	for(var i = 0, l = this.length; i < l; i++)
		this.push(this.splice(Math.floor(Math.random() * l), 1));
	return this;
}

// numbers and strings
Number.prototype.toInt = String.prototype.toInt = function(radix) {
	return parseInt(this, radix || 10);
}
Number.prototype.toFloat = String.prototype.toFloat = function() {
	return parseFloat(this);
}
String.prototype.trim = function() {
	return this.replace(/^\s+|\s+$/g, '');
}
String.prototype.camelCase = function() {
	return this.replace(/-\D/g, function(match) {
		return match.charAt(1).toUpperCase();
	});
}

//
GOOP.extend(window,['events']);
Browser.init();
window.addEvent('load',GOOP.onLoad);